#include    "OTA_Protocol.h"
#include    "md5.h"
#include    <stdio.h>
#include    <string.h>
#include    "usart.h"

#include "stm32f1xx.h"

#define PROTOCOL_DEBUG

#define	APPASIZE						(50*1024)							// APPA 固件文件大小
#define APPBSIZE                    	(50*1024)							// APPB 固件文件大小
#define FLASHPAGE						(1*1024)							// 低密度型STM32FLASH页容量  
#define OTAPKTDATASIZE					(256)								// OTA升级数据包固定长度

#define BOOTLOADERADDR					(0x08000000)						// bootloader起始地址
#define APPAFLASHADDR					(BOOTLOADERADDR + 12*1024)
#define APPBFLASHADDR					(APPAFLASHADDR + 50*1024)
#define	PUBLICFLASHADDR					(APPBFLASHADDR + 50*1024)			
	
#define UPDATEPARAADDR					(PUBLICFLASHADDR)

#define NEED_UPDATE						(1)
#define DONT_UPDATE						(0)

#define OTATIMEOUT						1000*60*1							// ms

#define OTASend(pdata,len)
#define	OTAWriteFlash(Addr,pdata,len)
#define OTAReadFlash(Addr,pdata,len)

#define ReadUpdateParaInfo()			OTAReadFlash(UPDATEPARAADDR, (uint8_t *)&UpdatePataInfo, sizeof(UpdateParaInfo_t))
#define	WriteUpdateParaInfo()			OTAWriteFlash(UPDATEPARAADDR, (uint8_t *)&UpdatePataInfo, sizeof(UpdateParaInfo_t))
#define ClearUpdateParaInfo()			memset1((uint8_t *)&UpdatePataInfo, 0, sizeof(UpdateParaInfo_t))

extern uint32_t DevMacAddr;
UpdateParaInfo_t UpdatePataInfo;
static MSGHeadInfo_t   MSGHeadInfo;
static MD5_CTX  MD5_Context;
static uint8_t OTAData[FLASHPAGE];
static uint32_t PktAddr = 0;


uint32_t OTATimeoutval;

static uint32_t CalcSum32(uint8_t *pdata, uint16_t len);
static void mcuRestart(void);
static uint8_t UpdateAPP(void);
static uint8_t CheckOTAFileMD5Valid(void);

static uint8_t ProtocolCmdOtaHandle(uint8_t *pdata, uint16_t len);
static uint8_t ProtocolCmdOtaDataHandle(uint8_t *pdata, uint16_t len);
static uint8_t ProtocolCmdOtaStateHandle(uint8_t *pdata, uint16_t len);



uint8_t ProtocolOtaHandle(uint8_t *pdata, uint16_t len)
{
    uint8_t res = 0;
    
    if(len < 7) 
    {
        return ERR_LEN;
    }
    
    MSGHeadInfo.cmd = (ProtocolCmdOtaEnum_e)(*pdata);
    switch( MSGHeadInfo.cmd )
    {
        case CMD_OTA:          
        case CMD_OTA_DATA:
        case CMD_OTA_STATE: res = 0; break;
        default: res = 1; break;
    }
    if(res) return res;
    
    MSGHeadInfo.devid = (*(pdata + 1) << 24) | (*(pdata + 2) << 16) | (*(pdata + 3) << 8) | (*(pdata + 4));
    MSGHeadInfo.cmdid = (*(pdata + 6) << 8) | (*(pdata + 5));
       
    switch( MSGHeadInfo.cmd )
    {
        case CMD_OTA:
            ProtocolCmdOtaHandle(pdata,len);
            break;            
        case CMD_OTA_DATA:
        	ProtocolCmdOtaDataHandle(pdata,len);
            break;
        case CMD_OTA_STATE:
        	ProtocolCmdOtaStateHandle(pdata,len);
            break;
        default: break;
    }
    return res;
}

static void mcuRestart(void)
{
    __set_FAULTMASK(1); 
    NVIC_SystemReset();
}

static uint8_t UpdateAPP(void)
{
	DEBUG_Printf("Systerm Restart... \r\n");
	UpdatePataInfo.updateflag = NEED_UPDATE;
	UpdatePataInfo.softver = UpdatePataInfo.otaupdate.otasoftver;
	memset1((uint8_t *)&UpdatePataInfo.otaupdate, 0, sizeof(OtaPktInfo_t));
	WriteUpdateParaInfo();
	mcuRestart();	
}

static uint8_t CheckOTAFileMD5Valid(void)
{

	uint8_t md5calc[16];
	uint32_t i;
	uint32_t rflashcnt;
	uint32_t remaincnt;

	if(UpdatePataInfo.otaupdate.otafilesize % FLASHPAGE)
	{
		rflashcnt = UpdatePataInfo.otaupdate.otafilesize / FLASHPAGE +1;
		remaincnt = rflashcnt*FLASHPAGE - UpdatePataInfo.otaupdate.otafilesize;
	}
	else
	{
		rflashcnt = UpdatePataInfo.otaupdate.otafilesize / FLASHPAGE;
		remaincnt = 0;
	}
	
	for(i = 0; i < rflashcnt; i++)
	{	
		if( (i == (rflashcnt-1)) && remaincnt)
		{
			memset(OTAData, 0, FLASHPAGE);
			OTAReadFlash(APPBFLASHADDR + i*FLASHPAGE, OTAData, remaincnt);
			MD5_Update(&MD5_Context, OTAData, remaincnt);
		}
		else
		{
			memset(OTAData, 0, FLASHPAGE);
			OTAReadFlash(APPBFLASHADDR + i*FLASHPAGE, OTAData, FLASHPAGE);
			MD5_Update(&MD5_Context, OTAData, FLASHPAGE);
		}
	}

	MD5_Final(&MD5_Context, md5calc);
	if( 0 != memcmp(UpdatePataInfo.otaupdate.otamd5buff, md5calc, 16))
	{
		DEBUG_Printf("OTA file MD5 check falid, MCU OTA faild\r\n");
#ifdef	PROTOCOL_DEBUG
		DEBUG_Printf("MD5 Calc: ");
		for( i = 0; i < 16; i++)
		{
			DEBUG_Printf("%02x ",md5calc[i]);
		}
		DEBUG_Printf("\r\n");
		DEBUG_Printf("MD5 serv: ");
		for( i = 0; i < 16; i++)
		{
			DEBUG_Printf("%02x ", UpdatePataInfo.otaupdate.otamd5buff[i]);
		}
		DEBUG_Printf("\r\n");
#endif
		return 1;
	}
	else
	{
		DEBUG_Printf("OTA file MD5 check success, ready to update\r\n");
		return 0;
	}

}

static uint32_t CalcSum32(uint8_t *pdata, uint16_t len)
{
    uint16_t i;
    uint32_t sum32 = 0;
    for( i = 0; i < len; i++)
    {
        sum32 += *(pdata++);
    }
    return sum32;
}

static uint8_t ProtocolAckOtaHandle(OtaExecResEnum_e execres)
{
    ProtocolAckOtaMsgType_t ackmsg = {0};
    ackmsg.cmd = MSGHeadInfo.cmd;
    ackmsg.devid = MSGHeadInfo.devid;
    ackmsg.cmdid = MSGHeadInfo.cmdid;
    ackmsg.execres = execres;
    OTASend((uint8_t *)&ackmsg, sizeof(ProtocolAckOtaMsgType_t));
}

static uint8_t ProtocolCmdOtaHandle(uint8_t *pdata, uint16_t len)
{
	uint8_t i;

    ProtocolCmdOtaMsgType_t *p_otamsg = NULL;
    p_otamsg = (ProtocolCmdOtaMsgType_t *)pdata;

    /*1 Check MSG Valid*/
    //check MSG length valid
    if( sizeof(ProtocolCmdOtaMsgType_t) != len)
    {
        return ERR_LEN;
    }
    //reload timeout    
	OTATimeoutval = OTATIMEOUT;
    //check MSG devmac valid
    if( MSGHeadInfo.devid != DevMacAddr)
    {
        ProtocolAckOtaHandle( EXE_ERRMAC );
        return ERR_MAC;
    }
    //check MSG payload valid
    if( CalcSum32(pdata+7, len - 7 - 4) != p_otamsg->payload.sum32)
    {
        ProtocolAckOtaHandle( EXE_ERRSUM );
        return ERR_SUM;
    }
    /* 2 OTA CMD Handle */
    if( UpdatePataInfo.softver >= p_otamsg->payload.softver)
    {
        ProtocolAckOtaHandle( EXE_ERRVER );
        DEBUG_Printf("OTA task creat failed, ota-file ver: %d, run-file ver: %d\r\n",p_otamsg->payload.softver,UpdatePataInfo.softver);
        UpdatePataInfo.otaupdate.otastate = OTA_IDLE;
        return 0;
    }
    if( p_otamsg->payload.filesize > APPBSIZE )
    {
        ProtocolAckOtaHandle( EXE_ERRSIZE );
        DEBUG_Printf("OTA task creat failed, ota-file size: %d  %.2fkB\r\n",p_otamsg->payload.filesize,p_otamsg->payload.filesize/1024);
        UpdatePataInfo.otaupdate.otastate = OTA_IDLE;
        return 0;
    }

	ProtocolAckOtaHandle( EXE_DEVREADY );
    UpdatePataInfo.otaupdate.otafilesize = p_otamsg->payload.filesize;
    UpdatePataInfo.otaupdate.otasoftver = p_otamsg->payload.softver;
    memcpy1(UpdatePataInfo.otaupdate.otamd5buff, p_otamsg->payload.md5buff, 16);
    UpdatePataInfo.otaupdate.otastate = OTA_RUN;

	DEBUG_Printf("OTA task creat success, ota-file ver: %d, ota-file size: %d\r\n",p_otamsg->payload.softver,p_otamsg->payload.filesize);

	DEBUG_Printf("OTA MD5: ");
    for(i = 0; i < 16; i++)
    {
    	DEBUG_Printf("%02x ", p_otamsg->payload.md5buff[i]);
    }
    DEBUG_Printf("\r\n");
    
}

static uint8_t ProtocolCmdOtaDataHandle(uint8_t *pdata, uint16_t len)
{

	ProtocolCmdOtaDataMsgType_t *p_otadata = NULL;
	p_otadata = (ProtocolCmdOtaDataMsgType_t *)pdata;

	 /*1 Check MSG Valid*/
    //check MSG length valid
    if( sizeof(ProtocolCmdOtaDataMsgType_t) != len)
    {
        return ERR_LEN;
    }
    //check MSG devmac valid
    if( MSGHeadInfo.devid != DevMacAddr)
    {
        ProtocolAckOtaHandle( EXE_ERRMAC );
        return ERR_MAC;
    }
    //check MSG payload valid
    if( CalcSum32(pdata+7, len - 7 - 4) != p_otadata->payload.sum32)
    {
        ProtocolAckOtaHandle( EXE_ERRSUM );
        return ERR_SUM;
    }
	/* 2 OTA DATA Handle */
	//check dev ota state
	if(UpdatePataInfo.otaupdate.otastate == OTA_IDLE)
	{
		DEBUG_Printf("OTA States is Wrong\r\n");
		return 0;
	}
	//reload timeout    
	OTATimeoutval = OTATIMEOUT;
	//save ota pkt to flash
	if(!p_otadata->payload.pktcnt)// the first ota packet
	{
		UpdatePataInfo.otaupdate.otapktall = p_otadata->payload.pktall;
		PktAddr = 0;
	}
	UpdatePataInfo.otaupdate.otapktcnt = p_otadata->payload.pktcnt;
	memcpy1(OTAData + (p_otadata->payload.pktcnt%4)*p_otadata->payload.len, p_otadata->payload.pktbuff, p_otadata->payload.len);
	if((p_otadata->payload.pktall-1) != p_otadata->payload.pktcnt)
	{
		if(p_otadata->payload.pktcnt%4 == 3)
		{
			OTAWriteFlash(APPBFLASHADDR + PktAddr, OTAData, FLASHPAGE);
			PktAddr += FLASHPAGE;
			memset1(OTAData, 0, FLASHPAGE);
		}
	}
	else
	{		
		OTAWriteFlash(APPBFLASHADDR + PktAddr, OTAData, ((p_otadata->payload.pktcnt%4)-1)*OTAPKTDATASIZE + p_otadata->payload.len);
		PktAddr = 0;
		memset1(OTAData, 0, FLASHPAGE);
		UpdatePataInfo.otaupdate.otastate = OTA_FINISHED;
	}
	
	DEBUG_Printf("OTA recv new packet: %d, totalpkt: %d, currentpkt: %d, progress finished: %.2f\r\n",p_otadata->payload.len,p_otadata->payload.pktall,p_otadata->payload.pktcnt,p_otadata->payload.pktcnt/p_otadata->payload.pktall);

	//give an ack to serv
	ProtocolAckOtaHandle( EXE_RECVDONE );

	
	if( OTA_FINISHED == UpdatePataInfo.otaupdate.otastate )
	{
		if(!CheckOTAFileMD5Valid())
		{
			UpdateAPP();
		}
	}
	
	return 0;
    
}

static uint8_t ProtocolCmdOtaStateHandle(uint8_t * pdata, uint16_t len)
{
    ProtocolAckOtaStateMsgType_t *p_otaack = NULL;
    p_otaack = (ProtocolAckOtaStateMsgType_t *)pdata;
    /*1 Check MSG Valid*/
    //check MSG length valid
    if( sizeof(ProtocolAckOtaStateMsgType_t) != len)
    {
        return ERR_LEN;
    }
    //reload timeout    
	OTATimeoutval = OTATIMEOUT;
    //check MSG devmac valid
    if( MSGHeadInfo.devid != DevMacAddr)
    {
        return ERR_MAC;
    }
	/* 2 OTA State Handle */
	if(p_otaack->execres != EXE_PKTVALID)
	{
		memset1((uint8_t *)&UpdatePataInfo.otaupdate,0,sizeof(UpdateParaInfo_t));
	}
	else
	{
		UpdatePataInfo.otaupdate.otastate = OTA_RUN;
	}
	return 0;
}




